<!DOCTYPE HTML>
<html lang="zh-Hans" class="sidebar-visible no-js light">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>逆变、协变与子类型，以及Rust - Rust 优秀博文</title>


        <!-- Custom HTML head -->
        
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff" />

        <link rel="icon" href="../../favicon.svg">
        <link rel="shortcut icon" href="../../favicon.png">
        <link rel="stylesheet" href="../../css/variables.css">
        <link rel="stylesheet" href="../../css/general.css">
        <link rel="stylesheet" href="../../css/chrome.css">
        <link rel="stylesheet" href="../../css/print.css" media="print">

        <!-- Fonts -->
        <link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="../../fonts/fonts.css">

        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="../../highlight.css">
        <link rel="stylesheet" href="../../tomorrow-night.css">
        <link rel="stylesheet" href="../../ayu-highlight.css">

        <!-- Custom theme stylesheets -->

    </head>
    <body>
        <!-- Provide site root to javascript -->
        <script type="text/javascript">
            var path_to_root = "../../";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script type="text/javascript">
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script type="text/javascript">
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('no-js')
            html.classList.remove('light')
            html.classList.add(theme);
            html.classList.add('js');
        </script>

        <!-- Hide / unhide sidebar before it is displayed -->
        <script type="text/javascript">
            var html = document.querySelector('html');
            var sidebar = 'hidden';
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            }
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">1.</strong> 类型系统</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../01_类型系统/Rust_Concept_Clarification_Deref_vs_AsRef_vs_Borrow_vs_Cow/Rust_Concept_Clarification_Deref_vs_AsRef_vs_Borrow_vs_Cow.html"><strong aria-hidden="true">1.1.</strong> Rust Concept Clarification Deref vs AsRef vs Borrow vs Cow</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust_Concept_Clarification_Deref_vs_AsRef_vs_Borrow_vs_Cow/Deref_AsRef_Borrow_Cow释义.html"><strong aria-hidden="true">1.2.</strong> Deref AsRef Borrow Cow 释义</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust的Borrow和AsRef：让你的代码用起来像呼吸一样自然/Rust的Borrow和AsRef：让你的代码用起来像呼吸一样自然.html"><strong aria-hidden="true">1.3.</strong> Rust的Borrow和AsRef：让你的代码用起来像呼吸一样自然</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust的Cow类型有什么用？详解Cow及其用途/Rust的Cow类型有什么用？详解Cow及其用途.html"><strong aria-hidden="true">1.4.</strong> Rust的Cow类型有什么用？详解Cow及其用途</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/判别Fn、FnMut、FnOnce的标准/判别Fn、FnMut、FnOnce的标准.html"><strong aria-hidden="true">1.5.</strong> 判别Fn、FnMut、FnOnce的标准</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/一行代码告诉你内部可变性的真相(UnsafeCell)/一行代码告诉你内部可变性的真相(UnsafeCell).html"><strong aria-hidden="true">1.6.</strong> 一行代码告诉你内部可变性的真相(UnsafeCell)</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Tour_of_Rust's_Standard_Library_Traits/Tour_of_Rust's_Standard_Library_Traits.html"><strong aria-hidden="true">1.7.</strong> Tour of Rust's Standard Library Traits</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/逆变、协变与子类型，以及Rust/逆变、协变与子类型，以及Rust.html" class="active"><strong aria-hidden="true">1.8.</strong> 逆变、协变与子类型，以及Rust</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust自引用结构、Pin与Unpin/Rust自引用结构、Pin与Unpin.html"><strong aria-hidden="true">1.9.</strong> Rust自引用结构、Pin与Unpin</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/为什么Rust需要Pin,Unpin/为什么Rust需要Pin,Unpin.html"><strong aria-hidden="true">1.10.</strong> 译：为什么 Rust 需要 Pin, Unpin ？</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/定海神针Pin和Unpin/定海神针Pin和Unpin.html"><strong aria-hidden="true">1.11.</strong> 译：定海神针 Pin 和 Unpin</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/sizedness-in-rust/sizedness-in-rust.html"><strong aria-hidden="true">1.12.</strong> Sizedness in Rust</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust生命周期集大成者PhantomData〈T〉/Rust生命周期集大成者PhantomData〈T〉.html"><strong aria-hidden="true">1.13.</strong> Rust生命周期集大成者 PhantomData&lt;T&gt;</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：用Rust做类型体操_Part_0.html"><strong aria-hidden="true">1.14.</strong> 数据库表达式执行的黑魔法：用Rust做类型体操 Part 0</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：GAT实现引用类型关联_Part_1.html"><strong aria-hidden="true">1.15.</strong> 数据库表达式执行的黑魔法：GAT实现引用类型关联 Part 1</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：用HRTB写bound_Part_2.html"><strong aria-hidden="true">1.16.</strong> 数据库表达式执行的黑魔法：用HRTB写bound Part 2</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：用Rust做类型体操之用宏展开重复代码_Part_3_&_4.html"><strong aria-hidden="true">1.17.</strong> 数据库表达式执行的黑魔法：用Rust做类型体操之用宏展开重复代码 Part 3 & 4</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：与Rust编译器斗智斗勇之表达式向量化_Part_5_&_6.html"><strong aria-hidden="true">1.18.</strong> 数据库表达式执行的黑魔法：与Rust编译器斗智斗勇之表达式向量化 Part 5 & 6</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：在Rust中用宏关联逻辑类型和实际类型_Part_7.html"><strong aria-hidden="true">1.19.</strong> 数据库表达式执行的黑魔法：在Rust中用宏关联逻辑类型和实际类型 Part 7</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">2.</strong> 生命周期</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../02_生命周期/Rust中的生命周期——从StrSplit实例说开去/Rust中的生命周期——从StrSplit实例说开去.html"><strong aria-hidden="true">2.1.</strong> Rust中的生命周期——从StrSplit实例说开去</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/与ChatGPT深度对话来学Rust生命周期/与ChatGPT深度对话来学Rust生命周期.html"><strong aria-hidden="true">2.2.</strong> 与ChatGPT深度对话来学Rust生命周期</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——early_bound与late_bound（1）/进击的Rust生命周期——early_bound与late_bound（1）.html"><strong aria-hidden="true">2.3.</strong> 进击的Rust生命周期——early_bound与late_bound（1）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——early_bound与late_bound（2）/进击的Rust生命周期——early_bound与late_bound（2）.html"><strong aria-hidden="true">2.4.</strong> 进击的Rust生命周期——early_bound与late_bound（2）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——一力降十会的MIR（1）/进击的Rust生命周期——一力降十会的MIR（1）.html"><strong aria-hidden="true">2.5.</strong> 进击的Rust生命周期——一力降十会的MIR（1）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——一力降十会的MIR（2）/进击的Rust生命周期——一力降十会的MIR（2）.html"><strong aria-hidden="true">2.6.</strong> 进击的Rust生命周期——一力降十会的MIR（2）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/Common_Rust_Lifetime_Misconceptions/Common_Rust_Lifetime_Misconceptions.html"><strong aria-hidden="true">2.7.</strong> Common Rust Lifetime Misconceptions</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/Rust生命周期常见误区/Rust生命周期常见误区.html"><strong aria-hidden="true">2.8.</strong> 译：Rust生命周期常见误区</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">3.</strong> 无畏并发</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../05_无畏并发/简单写个Rust无锁队列/简单写个Rust无锁队列.html"><strong aria-hidden="true">3.1.</strong> 简单写个Rust无锁队列</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/进击的Rust多线程——混合自旋锁/进击的Rust多线程——混合自旋锁.html"><strong aria-hidden="true">3.2.</strong> 进击的Rust多线程——混合自旋锁</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/An_unsafe_tour_of_Rust's_Send_and_Sync/An_unsafe_tour_of_Rust's_Send_and_Sync.html"><strong aria-hidden="true">3.3.</strong> An unsafe tour of Rust's Send and Sync</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/进击的Rust多线程——Send与Sync/进击的Rust多线程——Send与Sync.html"><strong aria-hidden="true">3.4.</strong> 进击的Rust多线程——Send与Sync</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/进击的Rust多线程——离经叛道的PhantomData/进击的Rust多线程——离经叛道的PhantomData.html"><strong aria-hidden="true">3.5.</strong> 进击的Rust多线程——离经叛道的PhantomData</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/Rust_Async_Pin概念解析/Rust_Async_Pin概念解析.html"><strong aria-hidden="true">3.6.</strong> Rust Async: Pin概念解析</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/Rust和C++的并发库对比/Rust和C++的并发库对比.html"><strong aria-hidden="true">3.7.</strong> 译：Rust 和 C++ 的并发库对比</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/Rust原子类型和内存排序/Rust原子类型和内存排序.html"><strong aria-hidden="true">3.8.</strong> Rust原子类型和内存排序</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">4.</strong> 网络编程</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../06_网络编程/从编解码层面理解WebSocket_手写一个WebSocket/从编解码层面理解WebSocket_手写一个WebSocket.html"><strong aria-hidden="true">4.1.</strong> 从编解码层面理解WebSocket 手写一 个WebSocket</a></li><li class="chapter-item expanded "><a href="../../06_网络编程/透过Rust探索系统的本原：网络篇/透过Rust探索系统的本原：网络篇.html"><strong aria-hidden="true">4.2.</strong> 透过Rust探索系统的本原：网络篇</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">5.</strong> 轮子系列</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../07_轮子系列/700行Rust写一个内存分配器/700行Rust写一个内存分配器.html"><strong aria-hidden="true">5.1.</strong> 700行Rust写一个内存分配器</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/Rust：网络库的实现思路/Rust：网络库的实现思路.html"><strong aria-hidden="true">5.2.</strong> Rust：网络库的实现思路</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/Rust异步运行时基础部件/Rust异步运行时基础部件.html"><strong aria-hidden="true">5.3.</strong> Rust异步运行时基础部件</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/使用Rust+epoll编写异步IO框架/使用Rust+epoll编写异步IO框架（1）.html"><strong aria-hidden="true">5.4.</strong> 使用Rust+epoll编写异步IO框架（1）</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/使用Rust+epoll编写异步IO框架/使用Rust+epoll编写异步IO框架（2）.html"><strong aria-hidden="true">5.5.</strong> 使用Rust+epoll编写异步IO框架（2）</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/使用Rust+epoll编写异步IO框架/使用Rust+epoll编写异步IO框架（3）.html"><strong aria-hidden="true">5.6.</strong> 使用Rust+epoll编写异步IO框架（3）</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day1.html"><strong aria-hidden="true">5.7.</strong> 用rust从零开发一套web框架：day1</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day2.html"><strong aria-hidden="true">5.8.</strong> 用rust从零开发一套web框架：day2</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day3.html"><strong aria-hidden="true">5.9.</strong> 用rust从零开发一套web框架：day3</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day4.html"><strong aria-hidden="true">5.10.</strong> 用rust从零开发一套web框架：day4</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day5.html"><strong aria-hidden="true">5.11.</strong> 用rust从零开发一套web框架：day5</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">6.</strong> 奇技淫巧</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../08_奇技淫巧/Copy-On-Write是不是优化？/Copy-On-Write是不是优化？.html"><strong aria-hidden="true">6.1.</strong> 译：Copy-On-Write是不是优化？</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/揭秘神奇的Rust_Axum风格的函数实现/揭秘神奇的Rust_Axum风格的函数实现.html"><strong aria-hidden="true">6.2.</strong> 译：揭秘神奇的 Rust Axum 风格的函数实现</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/“变长参数”函数与回调/“变长参数”函数与回调.html"><strong aria-hidden="true">6.3.</strong> “变长参数”函数与回调</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/Rust字符串格式化的幕后：format_args!()/Rust字符串格式化的幕后：format_args!().html"><strong aria-hidden="true">6.4.</strong> 译：Rust字符串格式化的幕后：format_args!()</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/给Rust带来一点C++特产/给Rust带来一点C++特产.html"><strong aria-hidden="true">6.5.</strong> 给Rust带来一点C++特产</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/一步步实现_Rust_Bevy_ECS_的_System_简化版本/一步步实现_Rust_Bevy_ECS_的_System_简化版本.html"><strong aria-hidden="true">6.6.</strong> 一步步实现 Rust Bevy ECS 的 System 简化版本</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/Exploring_Design_Patterns_in_Rust_with_Algorithmic_Trading_Examples/Exploring_Design_Patterns_in_Rust_with_Algorithmic_Trading_Examples.html"><strong aria-hidden="true">6.7.</strong> Exploring Design Patterns in Rust with Algorithmic Trading Examples</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">7.</strong> 源码分析</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../09_源码分析/Rust并发：bytes源码分析/Rust并发：bytes源码分析.html"><strong aria-hidden="true">7.1.</strong> Rust并发：bytes源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust并发：标准库Arc源码分析/Rust并发：标准库Arc源码分析.html"><strong aria-hidden="true">7.2.</strong> Rust并发：标准库Arc源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust并发：标准库sync_Once源码分析/Rust并发：标准库sync_Once源码分析.html"><strong aria-hidden="true">7.3.</strong> Rust并发：标准库sync::Once源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust源码阅读：引用计数Rc/Rust源码阅读：引用计数Rc.html"><strong aria-hidden="true">7.4.</strong> Rust源码阅读：引用计数Rc</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust源码阅读：Cell、RefCell与内部可变性/Rust源码阅读：Cell、RefCell与内部可变性.html"><strong aria-hidden="true">7.5.</strong> Rust源码阅读： Cell、RefCell与内部可变性</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/关于_Rust_的_UnsafeCell、Cell_与_RefCell/关于_Rust_的_UnsafeCell、Cell_与_RefCell.html"><strong aria-hidden="true">7.6.</strong> 关于 Rust 的 UnsafeCell、Cell 与 RefCell</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust_Async_async-stream源码分析/Rust_Async_async-stream源码分析.html"><strong aria-hidden="true">7.7.</strong> Rust Async: async-stream源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/走进Tokio的异步世界/走进Tokio的异步世界.html"><strong aria-hidden="true">7.8.</strong> 走进 Tokio 的异步世界</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/tokio.rs_runtime的实现/tokio.rs_runtime的实现.html"><strong aria-hidden="true">7.9.</strong> tokio.rs runtime 的实现</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Tokio_internals/Tokio_internals.html"><strong aria-hidden="true">7.10.</strong> Tokio internals</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Tokio_internals/译文：Tokio內部机制.html"><strong aria-hidden="true">7.11.</strong> 译：Tokio 内部机制</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust_Axum_HTTP_框架的架构分析/Rust_Axum_HTTP_框架的架构分析.html"><strong aria-hidden="true">7.12.</strong> Rust Axum HTTP 框架的架构分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/安利一个Rust_Game_Engine：Bevy--ECS部分/安利一个Rust_Game_Engine：Bevy--ECS部分.html"><strong aria-hidden="true">7.13.</strong> 安利一个Rust Game Engine：Bevy--ECS部分</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Tokio_解析之任务调度/Tokio_解析之任务调度.html"><strong aria-hidden="true">7.14.</strong> Tokio 解析之任务调度</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">8.</strong> 生态观察</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../11_生态观察/Rust_web_frameworks_have_subpar_error_reporting/Rust_web_frameworks_have_subpar_error_reporting.html"><strong aria-hidden="true">8.1.</strong> Rust web frameworks have subpar error reporting</a></li><li class="chapter-item expanded "><a href="../../11_生态观察/SeaORM：要做Rust版本的ActiveRecord/SeaORM：要做Rust版本的ActiveRecord.html"><strong aria-hidden="true">8.2.</strong> SeaORM：要做Rust版本的ActiveRecord</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">9.</strong> 死灵终极</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_总述.html"><strong aria-hidden="true">9.1.</strong> 译：Learn Rust the Dangerous Way 总述</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_0.html"><strong aria-hidden="true">9.2.</strong> 译：Learn Rust the Dangerous Way 0</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_1.html"><strong aria-hidden="true">9.3.</strong> 译：Learn Rust the Dangerous Way 1</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_2.html"><strong aria-hidden="true">9.4.</strong> 译：Learn Rust the Dangerous Way 2</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_3.html"><strong aria-hidden="true">9.5.</strong> 译：Learn Rust the Dangerous Way 3</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_4.html"><strong aria-hidden="true">9.6.</strong> 译：Learn Rust the Dangerous Way 4</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_5.html"><strong aria-hidden="true">9.7.</strong> 译：Learn Rust the Dangerous Way 5</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测（一）.html"><strong aria-hidden="true">9.8.</strong> Unsafe Rust 随堂小测（一）</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测（二）.html"><strong aria-hidden="true">9.9.</strong> Unsafe Rust 随堂小测（二）</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测（三）.html"><strong aria-hidden="true">9.10.</strong> Unsafe Rust 随堂小测（三）</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测参考答案.html"><strong aria-hidden="true">9.11.</strong> Unsafe Rust 随堂小测参考答案</a></li></ol></li></ol>
            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
        </nav>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky bordered">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">Rust 优秀博文</h1>

                    <div class="right-buttons">
                        <a href="../../print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>

                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>

                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script type="text/javascript">
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <h1 id="逆变协变与子类型以及rust"><a class="header" href="#逆变协变与子类型以及rust">逆变、协变与子类型，以及Rust</a></h1>
<p>作者：<a href="https://www.zhihu.com/people/quanbrew">知乎用户3ZGB97</a></p>
<p>原载：<a href="https://ioover.net/dev/variance-and-subtyping">https://ioover.net/dev/variance-and-subtyping</a>、<a href="https://zhuanlan.zhihu.com/p/41814387">https://zhuanlan.zhihu.com/p/41814387</a></p>
<p>这篇文章先发在我的博客上的：<a href="https://link.zhihu.com/?target=https%3A//ioover.net/dev/variance-and-subtyping/">逆变、协变与子类型，以及Rust · I/O OVER</a></p>
<p>学习C#（未遂）的时候碰到逆变和协变的概念。本来以为没有继承的语言不需要太考虑这个，结果我错了。在Rust中又碰到这些概念。遂记录如下。</p>
<p>除了Rust的小节，都不需要了解相关语言知识，伪代码用类似TypeScript的语法书写。</p>
<h2 id="子类型"><a class="header" href="#子类型">子类型</a></h2>
<p>首先要讲的是子类型（Subtyping），子类型在生活中最常见到的就是面向对象概念中的继承了。比如说我们现在有一个 <code>Animal</code> 类，现在我们定义一个新的类叫做 <code>Cat</code>。<code>Animal</code> 只有一些简单的method比如说移动、进食，而 <code>Cat</code> 则附加上了爬树、卖萌等。</p>
<p>当我们基于 <code>Animal</code> 定义 <code>Cat</code> 的时候，<code>Cat</code> 相对于 <code>Animal</code> 的<a href="https://www.zhihu.com/question/22267682/answer/28974249">内涵增加了，而外延收缩</a>了。抛开这种头昏脑胀的词，可以这样认为，我们<strong>至少</strong>可以说一个猫是一个动物。所以猫是动物的子类型，记作 <code>Cat: Animal</code>。</p>
<p>因为猫至少是一个动物，那么对于所有需要任何动物的地方，我都可以给一只猫。</p>
<p>比如说任何动物都可以做薛定谔实验的牺牲品，但按照残忍的惯例，我们塞一只猫。</p>
<pre><code class="language-ts">function schrödinger(sample: Animal) -&gt; bool { ... }
let cat = new Cat();
const alive = schrödinger(cat);
</code></pre>
<p>也就是说，当 <code>T: U</code> 的时候，任何需要形式参数 <code>a: U</code> 的函数，我们都能给一个实际参数 <code>a: T</code> ——子类型<strong>至少</strong>可以被当作它的超类型。</p>
<h2 id="协变逆变不变"><a class="header" href="#协变逆变不变">协变、逆变、不变</a></h2>
<p>作为基础，需要稍微提起类型构造器（Type constructor）。类型构造器就是一些带有泛型/模板参数的类型。当填满了参数，才会成为一个实际的类型。比如说很简单的「笼子」就是 <code>Cage&lt;T&gt;</code>，其中 <code>T</code> 就是类型参数。还有一些常见的比如说 <code>List&lt;T&gt;</code>。</p>
<p>现在回顾一下，我们现在知道一些类型之间的关系，也即是说我们知道 <code>Cat</code> 是 <code>Animal</code> 的子类型。那么对于随意的（一元）类型构造器 <code>M</code>， <code>M&lt;Cat&gt;</code> 和 <code>M&lt;Animal&gt;</code> 可能会有什么关系呢？（<a href="https://link.zhihu.com/?target=https%3A//zh.wikipedia.org/wiki/%E5%8D%8F%E5%8F%98%E4%B8%8E%E9%80%86%E5%8F%98">Wiki</a>）</p>
<ul>
<li>协变（covariance）：<code>M&lt;Cat&gt;: M&lt;Animal&gt;</code> 它们维持内部参数的关系不变。</li>
<li>逆变（contravariance）：<code>M&lt;Animal&gt;: M&lt;Cat&gt;</code> 它们的关系被反转了。</li>
<li>不变（invariance）：两者没有任何子类型关系。</li>
</ul>
<p>直觉上来说，只要有协变就够了：</p>
<p>薛定谔想要一个笼子，里面装着一种动物，他不关心是什么动物（<code>Cage&lt;Animal&gt;</code>），你给薛定谔一只装着猫的笼子（<code>Cage&lt;Cat&gt;</code>），薛定谔把这个猫当作一种动物做实验。也就是说在需要 <code>Cage&lt;Animal&gt;</code> 的地方都可以给一个 <code>Cage&lt;Cat&gt;</code> 。</p>
<p>然而这是不对的。考虑这样一个情况：</p>
<pre><code class="language-ts">let cage: Cage&lt;Cat&gt; = new Cage();
function capture(x: Cage&lt;Animal&gt;) {
	x.inner = new Dog();
}
capture(cat);
</code></pre>
<p>因为协变规则，对 <code>capture</code> 来说笼子是 <code>Cage&lt;Animal&gt;</code>。往笼子里塞一个狗，完全没问题。但是对于调用者来说，笼子的类型还是 <code>Cage&lt;Cat&gt;</code> 。这就破坏了类型安全。你接下来的代码期望这是装猫的笼子，其实里面装了一个狗。</p>
<p>所以如果一个容器是只读的，才能协变。不然很容易就能把一些特殊的容器协变到更一般的容器，再往里面塞进不应该塞的类型。</p>
<p>考虑 <code>Cage&lt;T&gt;</code> 对 <code>T</code> 逆变的情况，<code>Cage&lt;Animal&gt;: Cage&lt;Dog&gt;</code> 。也就是说当函数需要 <code>Cage&lt;Dog&gt;</code>的时候，总能传给函数一个 <code>Cage&lt;Animal&gt;</code>，函数当作 <code>Cage&lt;Dog&gt;</code> 来处理。</p>
<p>一般来说这很荒谬，<code>Cage&lt;Animal&gt;</code> 里面的动物可能是一只猫，强行当作一个狗来处理肯定会爆炸。但是对于上面的 <code>capture</code> 函数是有意义的，它不关心笼子里有什么，只往里面塞一个准备好的狗。也就是说对于只写的类型可以用逆变。</p>
<p>那么对于可读又可写的类型，当然就是不变了：我们不能做出任何假定，不然有可能爆炸。</p>
<p>还有一种特殊的类型，规则有点奇异，那就是函数类型。考虑一元函数，按照函数的箭头记法，把函数类型记作 <code>T -&gt; U</code>，其中 <code>T</code> 是逆变的而 <code>U</code> 是协变的。</p>
<p>返回值是协变的很好理解，我需要函数 <code>F</code> 最终返回一只动物，那么最终返回一只猫的函数是可接受的。（可以不断地扩大陪域）</p>
<p>参数是逆变可能有点奇怪了。考虑需要计算猫的年龄的情况： <code>Cat -&gt; Age</code>。给一个通用的，可以计算所有动物的年龄的函数 <code>Animal -&gt; Age</code> 来代替也是很好的。<code>Animal -&gt; Age</code> 的定义域 <code>Animal</code> 中那些 <code>Cat</code> 以外的值被裁掉了（我们只会传 <code>Cat</code>），就变成很棒的 <code>Cat -&gt; Age</code>。</p>
<p>所以任何时候，对需要一个一元函数 <code>T: U, T -&gt; V</code> 的情况，它的参数 <code>T</code> 可以用 <code>U</code> 来代替，只需要简单地无视 <code>U</code> 类型除 <code>T</code> 以外的取值就行了。</p>
<p>综合起来，也就是说：</p>
<p><img src="assets/001.jpg" alt="" /></p>
<h2 id="rust中的"><a class="header" href="#rust中的">Rust中的</a></h2>
<p>继承是狗屎。Rust中没有继承，所以没有逆变和协变！以上。</p>
<p>才怪。<em>子类型不一定是继承，逆变和协变也不一定是对于子类型的</em>。Rust中确实没有继承（不算Trait的话），结构体或者枚举之间没有子类型关系。</p>
<p>但是Rust中有lifetime，lifetime是和通常类型平行的另一套类型（另一个范畴），而Rust中的子类型就是对于lifetime而言的。<a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/nomicon/subtyping.html">这篇文章详细描述了lifetime的子类型及逆变协变</a>，本节只是笔记。</p>
<h2 id="子类型-1"><a class="header" href="#子类型-1">子类型</a></h2>
<p>子类型是一种序关系，不一定是像继承那样的超类型直接包含子类型（动物包含猫）。lifetime中，外层的lifetime是它所包含的内层lifetime的子类型： <code>'big: 'small</code>。所有人最初学到Rust中lifetime的子类型关系，都会对此感到困惑。</p>
<p><img src="assets/002.svg" alt="" /></p>
<p>这张图的子类型关系应当是 <code>'static: 'big: 'small_1</code>，<code>'static: 'big: 'small_2</code>。而 <code>'static</code> 则是所有lifetime的子类型。</p>
<p>lifetime就是作用域，作用域是很标准的嵌套关系，所以Rust的规则有点反直觉。对于集合，子集扩张到超集往往是恰当的，但一个作用域本身不应该被当作一个集合。我们可以说一只猫<strong>至少</strong>是一个动物。但对于lifetime，我不能说 <code>'small_1</code> <strong>至少</strong>是 <code>'big</code>，而应该说 <code>'big</code> <strong>至少</strong>是<code>'small_1</code>，也<strong>至少</strong>是 <code>'small_2</code>。</p>
<p>lifetime存在的意义就是界定资源<em>不应该超出一个范围</em>。也就是说扩张lifetime往往是危险的，而收缩（只读引用）lifetime是安全的。</p>
<p>如果遵循直觉，按照嵌套关系排列，也就是 <code>'small_1: 'big: 'static</code>。小的作用域 <code>'small_1</code> 就可以协变到全局作用域 <code>'static</code> ，那么在被读取的对象被销毁后，编译器还允许代码继续试图读取它，就会爆炸，整个lifetime系统就失效了。</p>
<p>几个方式来理解：</p>
<ol>
<li><code>'small_1</code> 的 lifetime 代表「包含 <code>'small_1</code> 的作用域的集合」；而 <code>'static</code> 就是「包含全局作用域的集合」只有一个元素。所以很显然后者是前者的子集，因为全局作用域包含了 <code>'small_1</code>。</li>
<li>越小的作用域，包含了它的外层作用域就越多。</li>
<li>lifetime类型所关联的作用域是内涵而不是外延，内涵增多则外延减少。正如 <code>Cat</code> 就是 <code>Animal</code> 增多内涵而来的，它的外延只有各种猫，而 <code>Animal</code> 的外延有各种动物。</li>
</ol>
<h2 id="逆变协变不变"><a class="header" href="#逆变协变不变">逆变、协变、不变</a></h2>
<p><a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/nomicon/subtyping.html">原文</a>详细地讲了这一块，简单记录一下。</p>
<p><code>&amp;</code> 和 <code>&amp;mut</code> 都是一个类型构造器，接受一个lifetime <code>'a</code> 和另一个类型 <code>T</code> 。</p>
<ul>
<li><code>&amp;'a T</code> 对 <code>'a</code> 和 <code>T</code> 协变。因为 <code>&amp;</code> 是只读的，传参数的时候，试图收缩lifetime是安全的。</li>
<li><code>&amp;'a mut T</code> 对 <code>'a</code> 协变，对 <code>T</code> 不变。这是唯一要多说说的。</li>
<li><code>fn(T) -&gt; U</code> 是对 <code>T</code> 逆变对 <code>U</code> 协变。原因和上文所述一样：当传入的参数是一个函数的时候，我们可以安全地收缩这个函数的定义域，扩张这个函数的陪域。除此处外Rust应该没有逆变。</li>
<li><code>Box</code>, <code>Vec</code> 和别的容器都是<strong>协变</strong>的。这在别的语言中会爆炸，但是Rust对可变性的限制导致可以安全地当作协变。当我们拿到一个容器的所有权的时候，外部别处就无法访问了，可以安全地对它协变而不用担心爆炸。</li>
<li><code>Cell&lt;T&gt;``RefCell&lt;T&gt;</code>，在内部是可读写的，所以是不变。</li>
</ul>
<p><code>&amp;'a mut T</code> 对 <code>'a</code> 协变，对 <code>T</code> 不变。是因为传参数的时候，收紧一个可变作用域的范围是安全的，调用者还维持着未收紧的作用域。</p>
<p>但是正如文中的例子一样：</p>
<pre><pre class="playground"><code class="language-rust">fn overwrite&lt;T: Copy&gt;(covarianced: &amp;mut T, short: &amp;mut T) {
    *covarianced = *short;
}

fn main() {
    let mut forever: &amp;'static str = &quot; 我会活到世界末日 &quot;;
    'small {
        let short = String::from(&quot; 我马上死了 &quot;);
        overwrite(&amp;mut forever, &amp;mut &amp;*short);
    }
    // 爆炸！用到了已经被释放的内存
    println!(&quot;{}&quot;, forever);
}
</code></pre></pre>
<p>如果在一次函数调用时，一个 <code>forever: &amp;mut &amp;'static T</code> 能够协变到 <code>covarianced: &amp;mut &amp;'small T</code> ，我们就可以把一个 <code>short: &amp;'small T</code> 存进协变后的参数 <code>covarianced: &amp;mut &amp;'small T</code>。</p>
<p>调用了对调用者来说，引用 <code>a</code> 的类型依然是 <code>&amp;mut &amp;'static T</code>，却存了一个更短命的引用 <code>short</code>。当 <code>short</code> 被销毁的时候，<code>a</code> 还维持着引用，就…会炸！</p>
<p>至于这里为什么不能用逆变，原因很简单，就是 <code>&amp;mut T</code> ，<code>T</code> 是可读可写的。如果能扩张<code>T</code> 的作用域（逆变），读取出来存到别的地方还是会炸。</p>
<blockquote>
<p>if variance would allow you to store a short-lived value in a longer-lived slot, then invariance must be used.</p>
</blockquote>
<p>如果使用逆变或者协变，将允许把短命的值塞进长寿的坑坑里面，就用不变（invariance）以阻止。</p>
<blockquote>
<p>More generally, the soundness of subtyping and variance is based on the idea that its ok to forget details, but with mutable references there’s always someone (the original value being referenced) that remembers the forgotten details and will assume that those details haven’t changed. If we do something to invalidate those details, the original location can behave unsoundly.</p>
</blockquote>
<p>更概括来说，对于子类型做逆变或者协变的健全性（<a href="https://link.zhihu.com/?target=https%3A//en.wikipedia.org/wiki/Soundness">soundness</a>）来源于，<em>被调用者</em>可以放心地忘记细节。遇到一个mutable引用代表有别人维持了那些被<em>被调用者</em>忽略的细节，并希望这些细节不会被改动。如果<em>被调用者</em>做了什么事使这些细节失效了，原本维持这些细节的地方就会爆炸。</p>
<p>编辑于 2018-08-11 18:27</p>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                            <a rel="prev" href="../../01_类型系统/Tour_of_Rust&#x27;s_Standard_Library_Traits/Tour_of_Rust&#x27;s_Standard_Library_Traits.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>

                            <a rel="next" href="../../01_类型系统/Rust自引用结构、Pin与Unpin/Rust自引用结构、Pin与Unpin.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                                <i class="fa fa-angle-right"></i>
                            </a>

                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                    <a rel="prev" href="../../01_类型系统/Tour_of_Rust&#x27;s_Standard_Library_Traits/Tour_of_Rust&#x27;s_Standard_Library_Traits.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>

                    <a rel="next" href="../../01_类型系统/Rust自引用结构、Pin与Unpin/Rust自引用结构、Pin与Unpin.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
            </nav>

        </div>




        <script type="text/javascript">
            window.playground_copyable = true;
        </script>


        <script src="../../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../mark.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../searcher.js" type="text/javascript" charset="utf-8"></script>

        <script src="../../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../highlight.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../book.js" type="text/javascript" charset="utf-8"></script>

        <!-- Custom JS scripts -->


    </body>
</html>
